package com.wdhcr.osspolicy.utils;

import lombok.SneakyThrows;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ReflectUtil {


    private static final String SET_PREFIX = "set";
    private static final String IS_PREFIX = "is";
    private static final String GET_PREFIX = "get";


    public static boolean isBasicType(String proType) {

        if ("class java.lang.Byte".equals(proType) || "class java.lang.Short".equals(proType) || "class java.lang.Integer".equals(proType) || "class java.lang.Long".equals(proType) || "class java.lang.Double".equals(proType) || "class java.lang.Float".equals(proType) || "class java.lang.Boolean".equals(proType) || ("class java.lang.String".equals(proType))) {
            return true;
        }
        if ("class java.util.Date".equals(proType) || "class java.math.BigDecimal".equals(proType)) {
            return true;
        }
        return false;
    }


    /**
     * 判断是否是List或者ArrayList
     *
     * @param field
     * @return
     */
    public static boolean isList(Field field) {
        boolean flag = false;
        String simpleName = field.getType().getSimpleName();
        if (simpleName.contains("List")) {
            flag = true;
        }
        return flag;
    }

    /**
     * 判断是否是set
     *
     * @param field
     * @return
     */
    public static boolean isSet(Field field) {
        boolean flag = false;
        String simpleName = field.getType().getSimpleName();
        if (simpleName.contains("Set")) {
            flag = true;
        }
        return flag;
    }

    /**
     * 判断是否是Map或者HashMap
     *
     * @param field
     * @return
     */
    public static boolean isMap(Field field) {
        boolean flag = false;
        String simpleName = field.getType().getSimpleName();
        if ("Map".equals(simpleName) || "HashMap".equals(simpleName)) {
            flag = true;
        }
        return flag;
    }

    /**
     * 检查object是否为java的基本数据类型
     *
     * @param clazz
     * @return
     */
    public static boolean checkObjectIsSysType(Object clazz) {
        String objType = clazz.getClass().toString();
        if ("byte".equals(objType) || "short".equals(objType) || "int".equals(objType) || "long".equals(objType) || "double".equals(objType) || "float".equals(objType) || "boolean".equals(objType)) {
            return true;
        } else {
            return false;
        }
    }


    public static void getAllFields(Class<?> clazz, List<Field> fieldList) {
        String classType = clazz.getTypeName();
        boolean sunFlag = classType.startsWith("java.") || classType.startsWith("javax.") || classType.startsWith("org.");
        if (clazz == null || checkObjectIsSysType(clazz) || sunFlag) {
            //如果object是null/基本数据类型，或者是sun公司提供的系统类，则不需要在递归调用
            return;
        }

        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            Type genericType = field.getGenericType();
            String proType = genericType.toString();

            if ("byte".equals(proType) || "short".equals(proType) || "int".equals(proType) || "long".equals(proType) || "double".equals(proType) || "float".equals(proType) || "boolean".equals(proType)) {
                fieldList.add(field);
                //属性是基本类型跳过
                continue;
            } else if (isBasicType(proType)) {
                fieldList.add(field);
                //属性是包装类跳过
                continue;
            } else if (isList(field) || isSet(field)) {
                if (genericType instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType) genericType;
                    Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
                    Class cl = (Class) actualTypeArgument;
                    getAllFields(cl, fieldList);
                }
            } else if (field.getType().isArray()) {
                Class<?> componentType = field.getType().getComponentType();
                getAllFields(componentType, fieldList);
            } else if (isMap(field)) {
                if (genericType instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType) genericType;
                    Type actualTypeArgument = parameterizedType.getActualTypeArguments()[1];
                    Class cl = (Class) actualTypeArgument;
                    getAllFields(cl, fieldList);
                }
            } else {
                Class<?> type = field.getType();
                getAllFields(type, fieldList);
            }
        }
    }

    @SneakyThrows
    public static void getAllFields(Object o, Map<Field,Object> fieldList) {
        Class<?> clazz = o.getClass();
        String classType = clazz.getTypeName();
        boolean sunFlag = classType.startsWith("java.") || classType.startsWith("javax.") || classType.startsWith("org.");
        if (o == null || checkObjectIsSysType(o) || sunFlag) {
            //如果object是null/基本数据类型，或者是sun公司提供的系统类，则不需要在递归调用
            return;
        }

        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            Type genericType = field.getGenericType();
            String proType = genericType.toString();

            if ("byte".equals(proType) || "short".equals(proType) || "int".equals(proType) || "long".equals(proType) || "double".equals(proType) || "float".equals(proType) || "boolean".equals(proType)) {
                fieldList.put(field,o);
                //属性是基本类型跳过
                continue;
            } else if (isBasicType(proType)) {
                fieldList.put(field,o);
                //属性是包装类跳过
                continue;
            } else if (isList(field)) {
                PropertyDescriptor descriptor = getPropertyDescriptor(clazz, field.getName());
                Method method = descriptor.getReadMethod();
                List list = (List) method.invoke(o);
                if (list != null && list.size() > 0) {
                    for (Object obj : list) {
                        // 递归
                        getAllFields(obj, fieldList);
                    }
                }
            } else if (isSet(field)) {
                PropertyDescriptor descriptor = getPropertyDescriptor(clazz, field.getName());
                Method method = descriptor.getReadMethod();
                Set set = (Set) method.invoke(o);
                if (set != null && set.size() > 0) {
                    for (Object obj : set) {
                        // 递归
                        getAllFields(obj, fieldList);
                    }
                }
            } else if (field.getType().isArray()) {
                Object[] objArr = (Object[]) field.get(o);
                if (objArr != null && objArr.length > 0) {
                    for (Object arr : objArr) {
                        //递归
                        getAllFields(arr, fieldList);
                    }
                }
            } else if (isMap(field)) {
                PropertyDescriptor descriptor = getPropertyDescriptor(clazz, field.getName());
                Method method = descriptor.getReadMethod();
                Map map = (Map) method.invoke(o);
                if (map != null && map.size() > 0) {
                    for (Object obj : map.values()) {
                        getAllFields(obj, fieldList);
                    }
                }
            } else {
                PropertyDescriptor descriptor = getPropertyDescriptor(clazz, field.getName());
                Method method = descriptor.getReadMethod();
                Object obj = method.invoke(o);
                if (obj != null) {
                    //递归
                    getAllFields(obj, fieldList);
                } else {
                    continue;
                }
            }
        }
    }


    public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName) {//根据需求，定制 自己的get和set方法
        Method setMethod = null;
        Method getMethod = null;
        PropertyDescriptor pd = null;
        try {
            Field field = clazz.getDeclaredField(propertyName);// 根据字段名来获取字段
            if (field != null) {
                // 构建方法的后缀
                String methodEnd = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
                setMethod = clazz.getDeclaredMethod(SET_PREFIX + methodEnd, new Class[]{field.getType()});
                // 构建get 方法
                getMethod = clazz.getDeclaredMethod(GET_PREFIX + methodEnd, new Class[]{});
                // 构建一个属性描述器 把对应属性 propertyName 的 get 和 set 方法保存到属性描述器中
                pd = new PropertyDescriptor(propertyName, getMethod, setMethod);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return pd;
    }


}
